//////////////////////////////////////////////
// Camera.h
//
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Forward decl -----------------------------

namespace nkGraphics
{
	class RenderContext ;

	enum class PROJECTION_TYPE ;
	enum class RENDERING_API ;
}

/// Includes ---------------------------------

// nkGraphics
#include "../Dll/DllDefines.h"

#include "../Graph/Node.h"

#include "Frustum.h"

// nkMaths
#include <NilkinsMaths/Algebra/Vector.h>
#include <NilkinsMaths/Algebra/Quaternion.h>
#include <NilkinsMaths/Algebra/Matrix.h>

// nkMemory
#include <NilkinsMemory/Containers/String.h>
#include <NilkinsMemory/Containers/StringView.h>

/// Class ------------------------------------
	
namespace nkGraphics
{
	class DLL_GRAPHICS_EXPORT Camera : public Node
	{		
		public :

			// Getters
			const Frustum& getFrustum () ;
			nkMemory::StringView getName () const ;
			PROJECTION_TYPE getProjectionType () const ;
			float getPerspectiveFov () const ;
			float getPerspectiveAspectRatio () const ;
			float getPerspectiveOffCenterFovLeft () const ;
			float getPerspectiveOffCenterFovRight () const ;
			float getPerspectiveOffCenterFovTop () const ;
			float getPerspectiveOffCenterFovBottom () const ;
			float getOrthographicHeight () const ;
			float getOrthographicAspectRatio () const ;
			float getNear () const ;
			float getFar () const ;
			bool getAutoUpdateOnContextSwitch () const ;
			bool getHidden () const ;
			bool getReverseZ () const ;
			bool getInfiniteZ () const ;
			const nkMaths::Matrix& getViewMatrix () const ;
			const nkMaths::Matrix& getProjectionMatrix () const ;
			nkMaths::Matrix getViewProjMatrix () const ;
			nkMaths::Vector getProjectionParameters () const ;

			// Setters
			virtual void setPositionRelative (const nkMaths::Vector& value) override ;
			virtual void setPositionAbsolute (const nkMaths::Vector& value) override ;
			virtual void translateRelative (const nkMaths::Vector& value) override ;
			virtual void translateAbsolute (const nkMaths::Vector& value) override ;
			virtual void setOrientationRelative (const nkMaths::Quaternion& value) override ;
			virtual void setOrientationAbsolute (const nkMaths::Quaternion& value) override ;
			virtual void rotateRelative (const nkMaths::Quaternion& value) override ;
			virtual void rotateAbsolute (const nkMaths::Quaternion& value) override ;
			virtual void setScaleRelative (const nkMaths::Vector& value) override ;
			virtual void setScaleAbsolute (const nkMaths::Vector& value) override ;
			virtual void applyScale (const nkMaths::Vector& value) override ;
			void setName (nkMemory::StringView name) ;
			void setAsPerspective (float fovRadian, float aspectRatioWidthOverHeight) ;
			void setAsPerspectiveReverseZ (float fovRadian, float aspectRatioWidthOverHeight) ;
			void setAsPerspectiveReverseInfiniteZ (float fovRadian, float aspectRatioWidthOverHeight) ;
			void setAsPerspectiveOffCenter (float fovLeftRadian, float fovRightRadian, float fovTopRadian, float fovBottomRadian) ;
			void setAsPerspectiveOffCenterReverseZ (float fovLeftRadian, float fovRightRadian, float fovTopRadian, float fovBottomRadian) ;
			void setAsPerspectiveOffCenterReverseInfiniteZ (float fovLeftRadian, float fovRightRadian, float fovTopRadian, float fovBottomRadian) ;
			void setAsOrthographic (float height, float aspectRatioWidthOverHeight) ;
			void setAsOrthographicReverseZ (float height, float aspectRatioWidthOverHeight) ;
			void setRange (float near, float far) ;
			void prepareForContext (RenderContext* context) ;
			void setAutoUpdateOnContextSwitch (bool value) ;
			void setHidden (bool value) ;

			// Computing
			void updateViewMatrix () ;
			void updateProjectionMatrix () ;
			void lookAt (const nkMaths::Vector& point, const nkMaths::Vector& enforcedUp = nkMaths::Vector(0, 0, 0, 0)) ;

			// Useful information
			void getNearPlanePoints (nkMaths::Vector& a, nkMaths::Vector& b, nkMaths::Vector& c, nkMaths::Vector& d) ;
			void getFarPlanePoints (nkMaths::Vector& a, nkMaths::Vector& b, nkMaths::Vector& c, nkMaths::Vector& d) ;
			void getFrustumCornersDirectionsWorld (nkMaths::Vector& a, nkMaths::Vector& b, nkMaths::Vector& c, nkMaths::Vector& d) ;
			void getFrustumCornersDirectionsView (nkMaths::Vector& a, nkMaths::Vector& b, nkMaths::Vector& c, nkMaths::Vector& d) ;

			// Raycast
			nkMaths::Vector getDirectionAtPixelWorld (int x, int y, RenderContext* fromContext = nullptr) ;
			nkMaths::Vector getDirectionAtScreenCoordWorld (float x, float y) ;
			nkMaths::Vector getDirectionAtPixelView (int x, int y, RenderContext* fromContext = nullptr) ;
			nkMaths::Vector getDirectionAtScreenCoordView (float x, float y) ;
				
			// Import / export methods
			virtual void exportClassToTree (nkExport::Node* rootNode) override ;
			virtual void importClassFromTree (nkExport::Node* rootNode) override ;

		public :

			// Statics
			static nkMemory::UniquePtr<Camera> create (System* system = nullptr) ;

		private :

			// Functions
			// Constructor
			Camera (System* system) noexcept ;

		private :
		
			// Attributes
			// Name for easy tracking
			nkMemory::String _name ;

			// Rendering info
			nkMaths::Matrix _viewMat ;
			nkMaths::Matrix _projMat ;

			// Clip information
			float _near ;
			float _far ;
			union {float _fov ; float _orthographicHeight ; float _fovLeft ;} ;
			union {float _aspectRatio ; float _fovRight ;} ;
			float _fovTop ;
			float _fovBottom ;

			// Current frustum
			Frustum _frustum ;

			// Found renderer for projection
			RENDERING_API _renderer ;

			// Projection requested
			PROJECTION_TYPE _projectionType ;

			// Flags
			bool _dirtyFrustum ;
			bool _autoUpdateOnContextSwitch ;

			// Resource-like
			bool _hidden ;
	} ;
}